import  socket
import  time
import  json
import os
import  struct
import threading
from config import  packet_type,packet_attr,server_conf
import pymysql 
import ssl
import logging

#打开数据库连接
db = server_conf.database
#创建游标对象
cursor = db.cursor()
#服务器端日志记录

logger = logging.getLogger(__name__)
logger.setLevel(level=logging.DEBUG)
file_handler = logging.FileHandler('./Sever_simulate/serverlog.log')
file_handler.setLevel(level = logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

class Server:
    def __init__(self):
        #1.创建套接字
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        #2.绑定本地信息
        self.sock.bind(("",6969))
        #将socket打包成ssl socket
        self.server = ssl.wrap_socket(self.sock,keyfile='./ssl/privkey.pem',certfile='./ssl/certificate.pem',server_side=True)

    def server_listen(self):
        #3.让默认的套接字由主动变成被动
        self.server.listen(3000)
        while True:
            client_socket,client_address = self.server.accept()
            #开启多线程,这里arg后面一定要跟逗号，否则报错
            thread = threading.Thread(target=self.conn_thread, args=(client_socket,))
            thread.start()

    #连接线程
    def conn_thread(self,connect):
        #服务器端根目录
        file_root_path = server_conf.root
        header_size = packet_attr.header_size
        #等待客户端的链接
        while True:
            #connect = socket.socket
            try:
                connect.settimeout(60)
                #接收来自客户端的请求报头
                recv_header = connect.recv(header_size)

                if recv_header:
                    pkt_type, body_size = struct.unpack('!2I',recv_header)
                    
                    #接受到文件请求数据包
                    if pkt_type == packet_type.FILE_REQUEST:
                        info_raw = connect.recv(body_size)
                        info_json = json.loads(info_raw)
                        file_name = info_json['filename']
                        user_name = info_json['username']
                        logger.info('用户:'+user_name+'请求下载文件:'+file_name)
                        file_path = file_root_path + user_name + '/' + file_name
                        print(file_path)
                        file_size = os.stat(file_path).st_size
                        #先发送元数据报文通知客户端准备缓存
                        body = bytes(json.dumps(dict(filename = file_name,filesize = file_size)).encode('utf-8'))
                        header = struct.pack('!2I',packet_type.FILE_METADATA,len(body))
                        meta_data_package = header+body
                        connect.send(meta_data_package)
                        
                        #正式发送文件
                        f = open(file_path,'rb')
                        logger.info('开始传送文件!')
                        while True:
                            file_data = f.read(packet_attr.max_body_size)
                            header = struct.pack('!2I',packet_type.FILE_CONTENT,len(file_data))
                            if not file_data:
                                break
                            data_packet = header + file_data
                            connect.send(data_packet)
                        f.close()
                        logger.info('文件传送完成！')

                    #客户端发送元数据报文，准备上传文件到服务器
                    elif pkt_type == packet_type.FILE_METADATA:
                        #解析元数据报文
                        meta_data_raw = connect.recv(body_size)
                        meta_data = json.loads(meta_data_raw)
                        file_size = meta_data["filesize"]
                        file_name = meta_data["filename"]
                        user_name = meta_data["username"]

                        logger.info('用户：'+user_name+'请求上传文件：'+file_name)

                        path = file_root_path+"/"+ user_name +"/" + file_name

                        #如果该路径已经存在
                        if os.path.exists(path):
                            #已存在的文件大小
                            current_file_size = os.stat(path).st_size
                            #给客户端发送通知，表明该文件已经存在,以及已经上传的文件大小
                            body = bytes(json.dumps(dict(info = "path already exsits!",current_size = current_file_size)).encode('utf-8'))
                            header = struct.pack('!2I',packet_type.UPLOAD_RESPONSE,len(body))
                            packet = header + body
                            connect.send(packet)
                            
                            
                            if current_file_size < file_size:
                                rest_size = file_size - current_file_size
                                logger.info('开始接收断点续传的文件！')
                                while rest_size > 0:
                                    #接受客户端上传的文件
                                    try:
                                        content_header = connect.recv(header_size)
                                    except Exception:
                                        continue
                                    pkt_type , body_size = struct.unpack('!2I',content_header)
                                    print(body_size)
                                    assert(pkt_type == packet_type.FILE_CONTENT)
                                    file_data = connect.recv(body_size)
                                    if file_data:
                                        with open(path,'ab') as f:
                                            f.write(file_data)
                                            rest_size -= body_size
                                assert(rest_size == 0)
                                logger.info('接收完成！')

                        else:
                            #通知客户端可以上传
                            body = bytes(json.dumps(dict(info = "OK!")).encode('utf-8'))
                            header = struct.pack('!2I',packet_type.UPLOAD_RESPONSE,len(body))
                            packet = header + body
                            connect.send(packet)
                            #要接收的剩余大小

                            rest_size = file_size
                            logger.info('开始接收上传的文件！')
                            while rest_size > 0:
                                #接受客户端上传的文件
                                try:
                                    content_header = connect.recv(header_size)
                                except Exception:
                                    continue
                                pkt_type , body_size = struct.unpack('!2I',content_header)
                                print(body_size)
                                assert(pkt_type == packet_type.FILE_CONTENT)
                                file_data = connect.recv(body_size)
                                if file_data:
                                    with open(path,'wb') as f:
                                        f.write(file_data)
                                        rest_size -= body_size
                            assert(rest_size == 0)
                            logger.info('接收完成！')
                    
                    #文件目录请求响应
                    elif pkt_type == packet_type.CATALOG_REQUEST:
                        user_name_raw = connect.recv(body_size)
                        user_name_json = json.loads(user_name_raw)
                        user_name = user_name_json['user_name']
                        #只能访问自己的文件夹
                        user_folder = file_root_path+"/"+user_name
                        logger.info('用户：'+user_name+'发起远程目录请求!')
                        filelist = []
                        for root,dirs,files in os.walk(user_folder):
                            print(root)
                            print(dirs)
                            print(files)
                            for file in files:
                                filelist.append(file)

                        
                        body = bytes(json.dumps(dict(file_list = filelist)).encode('utf-8'))
                        header = struct.pack('!2I',packet_type.CATALOG_RESPONSE,len(body))
                        packet = header +  body
                        connect.send(packet)
                        logger.info('远程目录已发送！')

                    #接收到注册请求
                    elif pkt_type == packet_type.REGISTER_REQUEST:
                        user_info_raw = connect.recv(body_size)
                        user_info = json.loads(user_info_raw)
                        user_name = user_info['user_name']
                        user_password = user_info['user_password']
                        logger.info('用户:'+user_name+'发起注册请求!')

                        sql = "select * from users where user_name = '%s'" % (user_name)
                        cursor.execute(sql)
                        record = cursor.fetchone()
                        
                        #如果用户已经注册
                        if record:
                            body = bytes(json.dumps(dict(info = "username already exist!")).encode('utf-8'))
                            header = struct.pack("!2I",packet_type.REGISTER_RESPONSE,len(body))
                            packet = header + body
                            connect.send(packet)
                            logger.warning('用户名已经存在！注册失败！')
                        else:
                            sql = "insert into users values ('%s','%s')"%(user_name,user_password)
                            cursor.execute(sql)
                            db.commit()
                            body = bytes(json.dumps(dict(info = "SUCCESS!")).encode('utf-8'))
                            header = struct.pack("!2I",packet_type.REGISTER_RESPONSE,len(body))
                            packet = header + body
                            connect.send(packet)
                            logger.info('注册成功！')
                            #注册成功后，在服务器端建立属于该用户的文件夹
                            os.mkdir(file_root_path+"/"+user_name)
                            logger.info('已建立用户专属文件夹!')

                    #接收到登陆请求
                    elif pkt_type == packet_type.LOGIN_REQUEST:
                        login_info_raw = connect.recv(body_size)
                        login_info = json.loads(login_info_raw)
                        user_name = login_info['user_name']
                        user_password = login_info['user_password']
                        logger.info('用户:'+user_name+'请求登陆!')

                        sql = "select * from users where user_name = '%s' and user_password = '%s'"%(user_name,user_password)
                        cursor.execute(sql)
                        data = cursor.fetchone()
                        if data:
                            body = bytes(json.dumps(dict(info = "LOGIN SUCCESS!")).encode('utf-8'))
                            header = struct.pack("!2I",packet_type.LOGIN_RESPONSE,len(body))
                            packet = header + body
                            connect.send(packet)
                            logger.info('登陆成功!')
                        else:
                            body = bytes(json.dumps(dict(info = "LOGIN FAIL!")).encode('utf-8'))
                            header = struct.pack("!2I",packet_type.LOGIN_RESPONSE,len(body))
                            packet = header + body
                            connect.send(packet)
                            logger.warning('登陆失败!')
            except socket.timeout:
                connect.close()
                break
            except ConnectionResetError:
                connect.close()
                break

     
if __name__ == "__main__":
    server = Server()
    server.server_listen()